/**************************************************************************** 
 * Meta，一个轻量级的静态分区Hypervisor 
 *
 * 版权(c) , 2022-
 *
 * 作者:
 *      Tupelo Shen <shenwanjiang2013@163.com>
 *
 * Meta是一个自由软件，你可以在遵循GNU GPLv2协议的条件下，重新修改并发布它。
 *
 ***************************************************************************/

#ifndef __PAGE_TABLE_H__
#define __PAGE_TABLE_H__

#include <meta.h>
#include <arch/page_table.h>

/****************************************************************************
 ************************** 公共变量声明 ************************************
 ****************************************************************************/
/* 页表描述符结构体 */
struct page_table_dscr {
    size_t  lvls;
    size_t* lvl_off;
    size_t* lvl_wdt;
    bool*   lvl_term;
};

struct page_table {
    pte_t*                  root;
    pte_t                   root_flags;
    struct page_table_dscr* dscr;
};

/****************************************************************************
 ************************** 公共函数接口 ************************************
 ****************************************************************************/
extern struct page_table_dscr* hyp_pt_dscr;
extern struct page_table_dscr* vm_pt_dscr;

pte_t*  pt_get_pte(struct page_table* pt, size_t lvl, vaddr_t va);
pte_t*  pt_get(struct page_table* pt, size_t lvl, vaddr_t va);
void    pte_set(pte_t* pte, paddr_t addr, pte_t flags);
bool    pte_check_rsw(pte_t* pte, pte_t flag);
void    pte_set_rsw(pte_t* pte, pte_t flag);
bool    pte_valid(pte_t* pte);
bool    pte_table(struct page_table* pt, pte_t* pte, size_t lvl);
bool    pte_page(struct page_table* pt, pte_t* pte, size_t lvl);
pte_t   pt_pte_type(struct page_table* pt, size_t lvl);

/* 获取某一级页表的项数 */
static inline size_t pt_nentries(struct page_table* pt, size_t lvl)
{
    return (1ULL << pt->dscr->lvl_wdt[lvl]) >> pt->dscr->lvl_off[lvl];
}

/* 计算页表自身占用内存大小 */
static inline size_t pt_size(struct page_table* pt, size_t lvl)
{
    return pt_nentries(pt, lvl) * sizeof(pte_t);
}
/* 获取pte的索引 */
static inline size_t pt_getpteindex(struct page_table* pt, pte_t* pte, size_t lvl)
{
    return (size_t)(((size_t)pte) & (pt_size(pt, lvl) - 1)) / sizeof(pte_t);
}

/* 根据虚拟地址获取pte的索引 */
static inline size_t pt_getpteindex_by_va(struct page_table* pt, vaddr_t va, 
    size_t lvl)
{
    return (va >> pt->dscr->lvl_off[lvl]) & (pt_nentries(pt, lvl) - 1);
}

static inline size_t pt_lvlsize(struct page_table* pt, size_t lvl)
{
    return 1ULL << pt->dscr->lvl_off[lvl];
}

static inline bool pt_lvl_terminal(struct page_table* pt, size_t lvl)
{
    return pt->dscr->lvl_term[lvl];
}

#endif /* __PAGE_TABLE_H__ */
